/**
 * 演示的案例： import 默认导入，改为按需导入
 * 1. 拿到原始代码 ， 读文件
 * 2. 字符串转成AST
 * 3. 分析AST 结构， 通过指定的钩子（@babel/core上下文提供的， 插件）
 * 4. 找到所有引用该声明的地方
 * 5. 修复各处引文，替换为期望的内容， 如果有重名，则使用 babel提供的函数，提供一个唯一的函数名
 * 6. 可以将生成后的代码，写入磁盘
 *
 * 参考地址： https://zhuanlan.zhihu.com/p/421190426?utm_id=0
 *
 *
 * 调试：
 * 全局安装 ts-node  会方便一点, vscode 参照 lanuch.json中配置即可， F5
 *
 */
import { transformFileAsync, PluginObj, NodePath } from '@babel/core'
import { declare } from '@babel/helper-plugin-utils'
import * as types from '@babel/types'

import path from 'node:path'
import fs from 'node:fs'

let target = `demo/01.js`

const basePlugin: PluginObj = {
	visitor: {
		Identifier(path) {
			// console.log(path);
		},
		ImportDeclaration(path) {
			// 使用@babel/helper-plugin-utils 提供的上下文，我们就可以使用 api.types.isImportDefaultSpecifier(item) 也是一样的效果

			// 我们使用 @babel/types 提供的方法，判断 标识符是否是 默认导入
			// 1. 拿到所有的默认导入
			const importDefaultSpecifiers = path.node.specifiers.filter((item) =>
				types.isImportDefaultSpecifier(item)
			)

			// console.log(importDefaultSpecifiers);

			// 2. 拿到它的作用域， 因为有同名等，需要限定修改期望的作用域内的变量
			importDefaultSpecifiers.forEach((item) => {
				// 拿到 import 进来的名字
				const importId = item.local.name

				console.log('变量名称', importId)

				// 拿到作用域内的声明, 一般是多个
				const binding = path.scope.getBinding(importId)

				// console.log(binding)

				const referedIds: Array<types.Identifier> = []
				const transformedIds: Array<types.Identifier> = []

				// 收集所有引用地方，还有声明的地方
				binding?.referencePaths.forEach((referencePath: NodePath) => {
					const currentPath = referencePath.parentPath

					if (currentPath) {
						const node = currentPath.node as types.MemberExpression
						const property = node.property as types.Identifier
						const methodName = property.name

						// 之前的方法名
						referedIds.push(property)

						// 如果作用域没有重名的变量
						if (!currentPath?.scope.getBinding(methodName)) {
							const methodNameNode = property
							currentPath?.replaceWith(methodNameNode)

							// 转换后的方法名
							transformedIds.push(methodNameNode)
						} else {
							// 重新生成一个变量名
							const newMethodName =
								referencePath.scope.generateUidIdentifier(methodName)

							currentPath.replaceWith(newMethodName)

							// 转换后的方法名
							transformedIds.push(newMethodName)
						}
					}
				})

				// 转换import 默认导入为 命名导入
				const newSpecifiers = referedIds.map((id, index) => {
					return types.importSpecifier(transformedIds[index], id)
				})

				// 替换为新的 AST 节点
				path.node.specifiers = newSpecifiers
			})
		},
	},
}

// getFileContent(target)

// 这里包裹的一个函数 declare 是提供友好的错误提示，辅助函数
// 这里可以要，也可以不要
const importTransformPlugin = declare((api, options, dirname) => {
	// 明确设置babel版本
	api.assertVersion(7)

	// 这块是 babel的插件写法
	return basePlugin
})

// 这里是不使用工具函数提供的api, 也是可以的
const importTansforPlugin2 = basePlugin

const run = async () => {
	console.log(path.join(__dirname, '../demo/01.js'))

	let res = await transformFileAsync(path.join(__dirname, '../demo/01.js'), {
		ast: true,
		// plugins: [importTransformPlugin],
		plugins: [importTansforPlugin2],
	})

	if (res && res.code) {
    console.log(res.code);

		// 直接拿到code, 做文件写入
		fs.writeFileSync(path.resolve(__dirname, '../demo/01-1.js'), res.code)
	}
}

run()
